home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 2
/
Amiga Tools 2.iso
/
golded
/
api
/
spell
/
main.c
next >
Wrap
C/C++ Source or Header
|
1995-02-17
|
20KB
|
702 lines
/* -----------------------------------------------------------------------------
GoldED API client example code, ©1995 Dietmar Eilert. Dice:
dcc main.c -// -proto -mi -r -2.0 -l reqtoolss.lib -o ram:spell
Note: Compiling this code requires reqtools includes & reqtools linker
libraries. The ReqTools library is © Nico François.
The following code adds spell checking capabilities to GoldED. It is based on
the ISpell package and the ISpell dictionary (available on Fish disks). ISpell
has to be installed properly (within a valid command path) before you can use
this API client. This example uses synchronous ARexx communication: Requests
are PutMsg()'ed to GoldED's port, followed by a WaitPort() to get the editor's
response. This works fine since we need no ARexx communication after the API
link has been established. If there were ARexx communication with GoldED AFTER
the link has been established (i.e. after sending the 'API PORT=...' command to
register with GoldED), we would have to use an asynchronous design beeing
capable of answering incoming API messages while waiting for completion of
ARexx requests sent to GoldED.
-------------------------------------------------------------------------------
*/
/// "includes"
#define Prototype extern
#include <exec/exec.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos/dos.h>
#include <dos/dostags.h>
#include <dos/rdargs.h>
#include <intuition/intuition.h>
#include <utility/tagitem.h>
#include <libraries/gadtools.h>
#include <rexx/errors.h>
#include <rexx/rxslib.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/graphics_protos.h>
#include <clib/intuition_protos.h>
#include <clib/diskfont_protos.h>
#include <clib/rexxsyslib_protos.h>
#include <clib/gadtools_protos.h>
#include <clib/reqtools_protos.h>
#include <clib/alib_protos.h>
#include <devices/audio.h>
#include "golded:api/include/golded.h"
Prototype void main(int, char **);
Prototype BOOL InitISpell(void);
Prototype void HandleAPI(BOOL, BOOL, char *);
Prototype ULONG *SendRexxCommand(char *, char *, struct MsgPort *, char *);
Prototype void CheckWord(struct APIMessage *, struct MsgPort *, BOOL, BOOL);
Prototype void Beep(void);
Prototype UWORD ComputeX(struct TextFont *, UWORD);
Prototype UWORD ComputeY(struct TextFont *, UWORD);
Prototype char *ShowSpell(char *, struct List *);
Prototype struct Node *SearchNode(struct List *, UWORD);
struct Library *ReqToolsBase;
///
/// "main"
void
main(int argc, char **argv)
{
const char *version = "$VER: spell 1.5 (" __COMMODORE_DATE__ ")";
struct RDArgs *rdArgs;
ULONG args[] = { 0, 0, 0 };
if (rdArgs = ReadArgs("ASK/S,NOFLASH/S,HOST/K/A", args, NULL)) {
if (ReqToolsBase = (struct ReqToolsBase *)OpenLibrary("reqtools.library", 0)) {
if (InitISpell())
HandleAPI(args[0], args[1], (char *)args[2]);
CloseLibrary(ReqToolsBase);
}
FreeArgs(rdArgs);
}
else
puts("syntax error: SPELL ASK/S,HOST/K/A");
exit(0);
}
///
/// "init"
/* -------------------------------- IntiISpell ---------------------------------
Prepare ISpell. Return TRUE if successful.
*/
BOOL
InitISpell()
{
if (FindPort("IRexxSpell"))
return(TRUE);
else {
struct TagItem tags[] = {SYS_Output, NULL, SYS_Input, NULL, SYS_Asynch, TRUE, TAG_DONE};
if (SystemTagList("ispell -r", tags) != -1) {
UWORD n;
for (n = 0; n < 100; n++, Delay(10))
if (FindPort("IRexxSpell"))
return(TRUE);
}
}
puts("ISpell not available");
return(FALSE);
}
///
/// "api management"
/* --------------------------------- HandleAPI ---------------------------------
Register with GoldED & handle incoming API messages.
*/
void
HandleAPI(ask, noFlash, host)
BOOL ask, noFlash;
char *host;
{
struct MsgPort *rexxPort, *apiPort;
if (apiPort = CreateMsgPort()) {
if (rexxPort = CreateMsgPort()) {
char command[255];
ULONG *result;
sprintf(command, "API PORT=%ld CLASS=%ld", apiPort, API_CLASS_ROOT | API_CLASS_KEY);
if (result = SendRexxCommand(host, command, rexxPort, NULL)) {
if (*result == RC_OK) {
BOOL active = TRUE;
do {
struct APIMessage *apiMsg, *nextMsg;
while (!(apiMsg = (struct APIMessage *)GetMsg(apiPort)))
WaitPort(apiPort);
do {
for (nextMsg = apiMsg; nextMsg; nextMsg = nextMsg->api_Next) {
if (nextMsg->api_State == API_STATE_NOTIFY) {
switch (nextMsg->api_Class) {
case API_CLASS_ROOT:
switch (nextMsg->api_Action) {
case API_ACTION_DIE:
active = FALSE;
break;
case API_ACTION_INTRODUCE:
static struct TagItem tags[] = {
API_Client_Name, "spell",
API_Client_Copyright, "©1995 Dietmar Eilert",
API_Client_Purpose, "Adds online spell checking",
TAG_DONE
};
nextMsg->api_Data = tags;
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
break;
case API_CLASS_KEY:
switch (nextMsg->api_Action) {
case API_ACTION_VANILLAKEY:
// checks are performed after white space characters:
if ((UBYTE)nextMsg->api_Data < 'A')
CheckWord(nextMsg, rexxPort, ask, noFlash);
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
break;
default:
nextMsg->api_Error = API_ERROR_UNKNOWN;
}
}
}
ReplyMsg((struct Message *)apiMsg);
} while (apiMsg = (struct APIMessage *)GetMsg(apiPort));
} while (active);
}
}
SendRexxCommand("IRexxSpell", "EXIT", rexxPort, NULL);
DeleteMsgPort(rexxPort);
}
DeleteMsgPort(apiPort);
}
}
///
/// "check word"
/* --------------------------------- CheckWord ---------------------------------
Check word. This function is called BEFORE GoldED actually inserts a white
space character (space, colon, ...) at the current cursor position. We assume
the current position to be a white space character to guarantee correct
behavior if the user inserts a character into a word. If ISpell complains about
a word we will either issue a beep (ask = FALSE) or additionally display a list
of suggestions (<ask> = TRUE).
Excerpt taken from man/ispell.1: If the word is not in the dictionary, but
there are near misses, then the result line contains an '&', a space, and a
list of the near misses separated by spaces.
*/
void
CheckWord(apiMsg, rexxPort, ask, noFlash)
struct APIMessage *apiMsg;
struct MsgPort *rexxPort;
BOOL ask, noFlash;
{
static UBYTE buffer[4096];
struct EditConfig *config;
UBYTE *next;
UWORD column;
config = apiMsg->api_Config;
column = config->Column;
// create copy of current line
movmem(config->CurrentBuffer, buffer, config->CurrentLen);
// cursor placed directly after word ?
if (column && (buffer[--column] > '@')) {
static char word[1000], result[1000];
UWORD wordLen;
// find beginning of word
for (next = buffer + column, wordLen = 1; column && (*(next - 1) > '@'); --column, --next)
++wordLen;
// create ISpell command line
movmem(next, word, wordLen);
word[wordLen] = 0;
strins(word, "QUICKCHECK ");
// make ISpell check word
if (SendRexxCommand("IRexxSpell", word, rexxPort, result)) {
if (stricmp(result, "ok")) {
if (noFlash)
Beep();
else
DisplayBeep(NULL);
// send additional CHECK command to get further information
if (ask && SendRexxCommand("IRexxSpell", word + 5, rexxPort, result)) {
if (*result == '&') {
// translate ISpell result string (suggestions) into exec list
struct List list;
struct Node *node, *nextNode;
char *start, *end, *selection;
for (NewList(&list), end = result + 2; *end;) {
for (start = end; *end && (*end != 32); ++end);
if (node = (struct Node *)AllocVec(sizeof(struct Node), MEMF_ANY | MEMF_CLEAR)) {
node->ln_Name = start;
AddTail(&list, node);
}
if (*end)
*end++ = 0;
}
// ask for user selection from list, insert user selection into text
if (selection = ShowSpell(apiMsg->api_Global->F_ScrnName, &list)) {
static struct APIModifyRequest modifyRequest;
UWORD newLen = strlen(selection);
if (newLen != wordLen)
movmem(next + wordLen, next + newLen, config->CurrentLen - (next - buffer));
movmem(selection, next, newLen);
// make GoldED change the current line
modifyRequest.mr_Next = NULL;
modifyRequest.mr_Line = config->Line;
modifyRequest.mr_Column = config->Column + (newLen - wordLen);
modifyRequest.mr_Size = config->CurrentLen + (newLen - wordLen);
modifyRequest.mr_Data = buffer;
apiMsg->api_Modify = &modifyRequest;
}
// free list of suggestions
for (node = list.lh_Head; nextNode = node->ln_Succ; node = nextNode)
FreeVec(node);
}
}
}
}
}
}
///
/// "gui"
/* --------------------------------- ShowSpell ---------------------------------
Show ISpell suggestions. Return user selection or NULL.
*/
char *
ShowSpell(screen, list)
char *screen;
struct List *list;
{
char *result = NULL;
struct Screen *scr;
if (scr = LockPubScreen(screen)) {
struct TextFont *font;
if (font = OpenDiskFont(scr->Font)) {
APTR visualInfo;
if (visualInfo = GetVisualInfoA(scr, NULL )) {
struct Gadget *context, *glist, *gad;
if (context = CreateContext(&glist)) {
UWORD ww, wh, offX, offY;
LONG displayWidth, displayHeight;
struct Window *win;
offX = scr->WBorLeft;
offY = scr->RastPort.TxHeight + scr->WBorTop + 1;
struct NewGadget newGad = {
offX,
offY,
ComputeX(font, 221),
ComputeY(font, 156),
NULL, scr->Font, 0, 0, visualInfo, 0
};
gad = CreateGadget(LISTVIEW_KIND, context, &newGad, GTLV_ShowSelected, NULL, GTLV_Labels, list, GTLV_Selected, 0, TAG_DONE);
rtGetVScreenSize(scr, &displayWidth, &displayHeight);
ww = ComputeX(font, 221);
wh = ComputeY(font, 156);
if (win = OpenWindowTags( NULL,
WA_Left, ((displayWidth - ww)>>1) - scr->ViewPort.DxOffset,
WA_Top, ((displayHeight - wh)>>1) - scr->ViewPort.DyOffset,
WA_Width, ww + offX + scr->WBorRight,
WA_Height, wh + offY + scr->WBorBottom,
WA_IDCMP, LISTVIEWIDCMP | IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_RAWKEY,
WA_Flags, WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE,
WA_Gadgets, glist,
WA_Title, "ISpell",
WA_PubScreen, scr,
TAG_DONE
)) {
ULONG class;
UWORD active;
active = 0;
result = list->lh_Head->ln_Name;
struct IntuiMessage *msg;
struct Node *node;
GT_RefreshWindow(win, NULL);
do {
while (!(msg = GT_GetIMsg(win->UserPort)))
WaitPort(win->UserPort);
switch (class = msg->Class) {
case RAWKEY:
WORD step = (msg->Code == CURSORUP) ? -1 : 1;
if (node = SearchNode(list, active + step)) {
active += step;
result = node->ln_Name;
GT_SetGadgetAttrs(gad, win, NULL, GTLV_Selected, active, GTLV_MakeVisible, active, TAG_DONE);
}
break;
case IDCMP_VANILLAKEY:
if (msg->Code == 27)
class = CLOSEWINDOW;
if (msg->Code == 13)
class = IDCMP_GADGETUP;
break;
case IDCMP_GADGETUP:
result = SearchNode(list, active = msg->Code)->ln_Name;
break;
}
GT_ReplyIMsg(msg);
} while ((class & (GADGETUP | CLOSEWINDOW)) == NULL);
if (class == CLOSEWINDOW)
result = NULL;
CloseWindow(win);
}
}
FreeVisualInfo(visualInfo);
}
}
UnlockPubScreen(screen, scr);
}
return(result);
}
/* --------------------------------- ComputeX ----------------------------------
Resize element of width <value> according to current font
*/
UWORD
ComputeX(font, value)
UWORD value;
struct TextFont *font;
{
return((font->tf_XSize * value) / 8);
}
/* --------------------------------- ComputeY ----------------------------------
Resize element of height <value> according to current font
*/
UWORD
ComputeY(font, value)
UWORD value;
struct TextFont *font;
{
return((font->tf_YSize * value) / 8);
}
///
/// "misc"
/* ---------------------------------- SearchNode --------------------------------
Return pointer to node if ordinal number (0, ...) is known.
*/
struct Node *
SearchNode(list, ordinal)
struct List *list;
UWORD ordinal;
{
struct Node *node;
for (node = list->lh_Head; node->ln_Succ; --ordinal, node = node->ln_Succ)
if (!ordinal)
return(node);
return(NULL);
}
/* ----------------------------------- Beep ------------------------------------
Short audible beep
*/
void
Beep()
{
struct IOAudio *audioIO;
if (audioIO = (struct IOAudio *)AllocVec(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) {
struct MsgPort *audioMP;
if (audioMP = CreateMsgPort()) {
UBYTE whichannel[] = { 1, 2, 4, 8 };
audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP;
audioIO->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
audioIO->ioa_Request.io_Command = ADCMD_ALLOCATE;
audioIO->ioa_Request.io_Flags = ADIOF_NOWAIT;
audioIO->ioa_AllocKey = 0;
audioIO->ioa_Data = whichannel;
audioIO->ioa_Length = sizeof(whichannel);
if (!OpenDevice("audio.device", 0, (struct IORequest *)audioIO, 0)) {
__chip const static UBYTE waveptr[2] = {127, -127};
audioIO->ioa_Request.io_Message.mn_ReplyPort = audioMP;
audioIO->ioa_Request.io_Command = CMD_WRITE;
audioIO->ioa_Request.io_Flags = ADIOF_PERVOL;
audioIO->ioa_Data = waveptr;
audioIO->ioa_Length = 2;
audioIO->ioa_Period = 1015;
audioIO->ioa_Volume = 32;
audioIO->ioa_Cycles = 60; // 44;
BeginIO((struct IORequest *)audioIO );
WaitPort(audioMP);
GetMsg (audioMP);
CloseDevice((struct IORequest *)audioIO);
}
DeleteMsgPort(audioMP);
}
FreeVec(audioIO);
}
}
///
/// "arexx"
/* ---------------------------------- SendRexxCommand -------------------------
Send ARexx message & wait for answer. Return pointer to result or NULL.
*/
ULONG *
SendRexxCommand(port, cmd, replyPort, buffer)
char *cmd, *port, *buffer;
struct MsgPort *replyPort;
{
struct MsgPort *rexxport;
Forbid();
if (rexxport = FindPort(port)) {
struct RexxMsg *rexxMsg, *answer;
if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {
if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {
static ULONG result;
rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;
PutMsg(rexxport, &rexxMsg->rm_Node);
do {
WaitPort(replyPort);
if (answer = (struct RexxMsg *)GetMsg(replyPort))
result = answer->rm_Result1;
} while (!answer);
Permit();
if (answer->rm_Result1 == RC_OK) {
if (answer->rm_Result2) {
if (buffer)
strcpy(buffer, (char *)answer->rm_Result2);
DeleteArgstring((char *)answer->rm_Result2);
}
}
DeleteArgstring((char *)ARG0(answer));
DeleteRexxMsg(answer);
return(&result);
}
}
}
Permit();
return(NULL);
}
///